package com.joker.demo.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.joker.demo.admin.domain.dto.JobInfoQueryDTO;
import com.joker.demo.admin.domain.dto.JobShardingParamItemDTO;
import com.joker.demo.admin.domain.po.JobInfoPO;
import com.joker.demo.admin.domain.po.JobLogPO;
import com.joker.demo.admin.domain.vo.JobInfoVO;
import com.joker.demo.admin.domain.vo.PageResult;
import com.joker.demo.admin.mapper.JobInfoMapper;
import com.joker.demo.admin.mapper.JobLogMapper;
import com.joker.demo.admin.service.JobGroupService;
import com.joker.demo.admin.service.JobInfoService;
import com.joker.demo.admin.util.JobConvertUtils;
import com.joker.demo.core.domain.dto.TaskExecuteRequestDTO;
import com.joker.demo.core.domain.enums.JobSchedulingTypeEnum;
import com.joker.demo.core.domain.enums.JobStatusEnum;
import com.joker.demo.core.scheduler.JobScheduler;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 任务信息服务实现类
 *
 * @author : feixiang.li
 * @since : 2025-07-25 18:05
 */
@Service
@Slf4j
public class JobInfoServiceImpl implements JobInfoService {

    @Resource
    private JobInfoMapper jobInfoMapper;

    @Resource
    private JobScheduler jobScheduler;


    @Resource
    private JobLogMapper jobLogMapper;


    @Resource
    private JobGroupService jobGroupService;




    @Override
    public List<JobInfoPO> queryJobInfo(JobInfoQueryDTO jobInfoQueryDTO) {
        if (Objects.isNull(jobInfoQueryDTO)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<JobInfoPO> queryWrapper = new LambdaQueryWrapper<>();
        if (CollUtil.isNotEmpty(jobInfoQueryDTO.getGroupJobNames())) {
            queryWrapper.in(JobInfoPO::getGroupJobName, jobInfoQueryDTO.getGroupJobNames());
        }
        if (Objects.nonNull(jobInfoQueryDTO.getTriggerStatus())) {
            queryWrapper.eq(JobInfoPO::getTriggerStatus, jobInfoQueryDTO.getTriggerStatus());
        }

        if (Objects.nonNull(jobInfoQueryDTO.getStartTime())) {
            queryWrapper.le(JobInfoPO::getNextTriggerTime, jobInfoQueryDTO.getStartTime());
        }
        if (Objects.nonNull(jobInfoQueryDTO.getLastTableSourceId())) {
            queryWrapper.gt(JobInfoPO::getId, jobInfoQueryDTO.getLastTableSourceId());
        }
        queryWrapper.orderBy(true, true, JobInfoPO::getId);
        // 分页
        if (jobInfoQueryDTO.getPageSize() != null) {
            queryWrapper.last("limit " + jobInfoQueryDTO.getPageSize());
        }
        // sql执行语句
        log.info("sql: {}", queryWrapper.getSqlSelect());
        return jobInfoMapper.selectList(queryWrapper);
    }

    @Override
    public PageResult<JobInfoVO> queryJobInfoPage(JobInfoQueryDTO jobInfoQueryDTO) {
        if (Objects.isNull(jobInfoQueryDTO)) {
            return PageResult.of(Collections.emptyList(), 0L, 1L, 10L);
        }

        // 构建查询条件
        LambdaQueryWrapper<JobInfoPO> queryWrapper = new LambdaQueryWrapper<>();
        if (CollUtil.isNotEmpty(jobInfoQueryDTO.getGroupJobNames())) {
            queryWrapper.in(JobInfoPO::getGroupJobName, jobInfoQueryDTO.getGroupJobNames());
        }
        if (Objects.nonNull(jobInfoQueryDTO.getTriggerStatus())) {
            queryWrapper.eq(JobInfoPO::getTriggerStatus, jobInfoQueryDTO.getTriggerStatus());
        }
        if (Objects.nonNull(jobInfoQueryDTO.getStartTime())) {
            queryWrapper.ge(JobInfoPO::getNextTriggerTime, jobInfoQueryDTO.getStartTime());
        }

        // 设置分页参数
        long current = jobInfoQueryDTO.getPageNum() != null ? jobInfoQueryDTO.getPageNum() : 1L;
        long size = jobInfoQueryDTO.getPageSize() != null ? jobInfoQueryDTO.getPageSize() : 10L;
        Page<JobInfoPO> page = new Page<>(current, size);

        // 按ID降序排序
        queryWrapper.orderByDesc(JobInfoPO::getId);

        // 执行分页查询
        IPage<JobInfoPO> pageResult = jobInfoMapper.selectPage(page, queryWrapper);

        // 转换为VO
        List<JobInfoVO> jobInfoVOList = pageResult.getRecords().stream().map(this::convertToVO).collect(Collectors.toList());

        return PageResult.of(jobInfoVOList, pageResult.getTotal(), current, size);
    }

    @Override
    public JobInfoVO getJobInfoDetail(Integer jobId) {
        if (Objects.isNull(jobId)) {
            return null;
        }

        JobInfoPO jobInfo = jobInfoMapper.selectById(jobId);
        if (jobInfo == null) {
            return null;
        }

        return convertToVO(jobInfo);
    }

    @Override
    public boolean triggerJob(Integer jobId) {
        if (Objects.isNull(jobId)) {
            return false;
        }
        // 这里需要调用调度器来手动触发任务
        log.info("手动触发任务，jobId: {}", jobId);
        JobInfoPO jobInfo = jobInfoMapper.selectById(jobId);
        handleSingleJobInfo(jobInfo, true);
        return true;
    }

    @Override
    public void handleSingleJobInfo(JobInfoPO jobInfo, boolean isManualTrigger) {
        //  如果支持分片参数,并且分片参数不为空，则根据分片参数来创建多个任务
        if (Objects.equals(jobInfo.getSharding(), 1) && StringUtils.isNotBlank(jobInfo.getShardingParams())) {
            String shardingParams = jobInfo.getShardingParams();
            List<JobShardingParamItemDTO> jobShardingParamItemList = JSON.parseArray(shardingParams, JobShardingParamItemDTO.class);
            int index = 0;
            List<JobLogPO> jobLogList = new ArrayList<>();
            for (JobShardingParamItemDTO jobShardingParamItemDTO : jobShardingParamItemList) {
                JobLogPO jobLog = JobConvertUtils.convertJobLogWithNoParam(jobInfo, jobShardingParamItemDTO.getRegisterKey());
                if (isManualTrigger) {
                    jobLog.setTriggerTime(new Date());
                }
                // 设置分片参数数据
                jobLog.setExecutorShardingIndex(index);
                jobLog.setExecutorShardingParam(jobShardingParamItemDTO.getShardingParam());
                // 分片参数如果没有绑定地址，则使用默认的生成的 地址
                String address = StringUtils.isBlank(jobShardingParamItemDTO.getRegisterKey()) ? jobGroupService.getAddressList(jobInfo) : jobShardingParamItemDTO.getRegisterKey();
                jobLog.setExecutorAddress(address);
                index++;
                jobLogList.add(jobLog);
            }
            jobLogMapper.insertOrUpdate(jobLogList);
            // 批量发送请求
            for (JobLogPO jobLog : jobLogList) {
                TaskExecuteRequestDTO taskExecuteRequestDTO = buildTaskExecuteRequestDTO(jobLog, isManualTrigger);
                jobScheduler.sendExecuteTask(taskExecuteRequestDTO);
            }
        } else {
            // 获取想要运行的地址
            String address = jobGroupService.getAddressList(jobInfo);
            JobLogPO jobLog = JobConvertUtils.convertJobLogWithNoParam(jobInfo, address);
            if (isManualTrigger) {
                jobLog.setTriggerTime(new Date());
            }
            jobLogMapper.insert(jobLog);
            TaskExecuteRequestDTO taskExecuteRequestDTO = buildTaskExecuteRequestDTO(jobLog, isManualTrigger);
            jobScheduler.sendExecuteTask(taskExecuteRequestDTO);
        }

        // 如果不是手动触发，则更新下下次运行时间
        if (!isManualTrigger) {
            JobConvertUtils.updateNextTriggerTime(jobInfo);
            jobInfoMapper.updateById(jobInfo);
        }
    }

    /**
     * 这里需要更新下一次的任务运行时间
     *
     * @param jobInfoList     任务信息
     * @param isManualTrigger 是否手动触发
     */
    @Override
    public void handleMultipleJobInfo(List<JobInfoPO> jobInfoList, boolean isManualTrigger) {
        if (CollUtil.isEmpty(jobInfoList)) {
            return;
        }
        List<JobLogPO> jobLogList = new ArrayList<>();
        Map<Long, String> addressListMap = jobGroupService.getAddressList(jobInfoList);
        for (JobInfoPO jobInfo : jobInfoList) {
            String addressList = addressListMap.get(jobInfo.getId());
            // 如果不是手动触发，则更新下下次运行时间
            if (!isManualTrigger) {
                JobConvertUtils.updateNextTriggerTime(jobInfo);
            }
            // 如果没有地址
            if (StringUtils.isBlank(addressList)) {
                log.info("任务没有可用地址，任务ID: {}", jobInfo);
                continue;
            }
            // 分为2类，第一类，支持分片参数
            if (Objects.equals(jobInfo.getSharding(), 1) && StringUtils.isNotBlank(jobInfo.getShardingParams())) {
                String shardingParams = jobInfo.getShardingParams();
                List<JobShardingParamItemDTO> jobShardingParamItemList = JSON.parseArray(shardingParams, JobShardingParamItemDTO.class);
                int index = 0;
                for (JobShardingParamItemDTO jobShardingParamItemDTO : jobShardingParamItemList) {
                    JobLogPO jobLog = JobConvertUtils.convertJobLogWithNoParam(jobInfo, jobShardingParamItemDTO.getRegisterKey());
                    if (isManualTrigger) {
                        jobLog.setTriggerTime(new Date());
                    }
                    // 设置分片参数数据
                    jobLog.setExecutorShardingIndex(index);
                    jobLog.setExecutorShardingParam(jobShardingParamItemDTO.getShardingParam());
                    // 分片参数如果没有绑定地址，则使用默认的生成的 地址
                    String address = StringUtils.isBlank(jobShardingParamItemDTO.getRegisterKey()) ? jobGroupService.getAddressList(jobInfo.getRouteStrategy(), addressList, jobInfo.getId()) : jobShardingParamItemDTO.getRegisterKey();
                    jobLog.setExecutorAddress(address);
                    index++;
                    jobLogList.add(jobLog);
                }
            } else {
                // 第二类不支持分片参数
                // 获取想要运行的地址
                String address = jobGroupService.getAddressList(jobInfo.getRouteStrategy(), addressList, jobInfo.getId());
                JobLogPO jobLog = JobConvertUtils.convertJobLogWithNoParam(jobInfo, address);
                if (isManualTrigger) {
                    jobLog.setTriggerTime(new Date());
                }
                jobLogList.add(jobLog);
            }
        }
        jobLogMapper.insertOrUpdate(jobLogList);
        // 更新下一次触发的时间
        jobInfoMapper.insertOrUpdate(jobInfoList);
        // 批量发送请求
        for (JobLogPO jobLog : jobLogList) {
            TaskExecuteRequestDTO taskExecuteRequestDTO = buildTaskExecuteRequestDTO(jobLog, isManualTrigger);
            jobScheduler.sendExecuteTask(taskExecuteRequestDTO);
        }
    }


    /**
     * 构建http请求参数
     *
     * @param jobLog job日志
     * @return 请求参数
     */
    private TaskExecuteRequestDTO buildTaskExecuteRequestDTO(JobLogPO jobLog, boolean triggerNow) {
        TaskExecuteRequestDTO taskExecuteRequestDTO = new TaskExecuteRequestDTO();
        taskExecuteRequestDTO.setJobId(jobLog.getJobId());
        taskExecuteRequestDTO.setJobLogId(jobLog.getId());
        taskExecuteRequestDTO.setJobName(jobLog.getExecutorHandler());
        taskExecuteRequestDTO.setRegisterKey(jobLog.getExecutorAddress());
        taskExecuteRequestDTO.setShardingParam(jobLog.getExecutorShardingParam());
        taskExecuteRequestDTO.setShardingParamIndex(jobLog.getExecutorShardingIndex());
        taskExecuteRequestDTO.setTriggerNow(triggerNow);
        if (triggerNow) {
            taskExecuteRequestDTO.setTriggerTime(new Date());
        } else {
            taskExecuteRequestDTO.setTriggerTime(jobLog.getTriggerTime());
        }
        return taskExecuteRequestDTO;
    }

    /**
     * 将PO转换为VO
     */
    private JobInfoVO convertToVO(JobInfoPO jobInfoPO) {
        JobInfoVO jobInfoVO = BeanUtil.copyProperties(jobInfoPO, JobInfoVO.class);

        // 设置状态描述
        if (jobInfoPO.getTriggerStatus() != null) {
            JobStatusEnum statusEnum = JobStatusEnum.getByCode(jobInfoPO.getTriggerStatus());
            if (statusEnum != null) {
                jobInfoVO.setTriggerStatusDesc(statusEnum.getDesc());
            }
        }

        // 设置调度类型描述
        if (jobInfoPO.getScheduleType() != null) {
            JobSchedulingTypeEnum typeEnum = JobSchedulingTypeEnum.getByCode(jobInfoPO.getScheduleType());
            if (typeEnum != null) {
                jobInfoVO.setScheduleTypeDesc(typeEnum.getDesc());
            }
        }

        return jobInfoVO;
    }
}
